#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2025 QuestionUploadSystem
# Author: QuestionUploadSystem Team
# License: MIT License
# Description: 格式转换器，用于解析和转换不同格式的题目文档

import re
import os
import logging
from typing import List, Dict, Tuple, Any, Optional
from pathlib import Path

from django.utils import timezone
from django.conf import settings

from .document_parsers import detect_answer, parse_multiple_choice_question, parse_multiple_questions
from .models import ParsedQuestion, ParserTask
from documents.models import Document

logger = logging.getLogger(__name__)

class FormatConverter:
    """
    格式转换器
    用于解析和转换不同格式的题目文档
    """
    
    def __init__(self, document=None, task=None):
        """
        初始化格式转换器
        
        Args:
            document (Document, optional): 文档对象
            task (ParserTask, optional): 解析任务对象
        """
        self.document = document
        self.task = task
        
        # 格式模式
        self.format_patterns = {
            'standard': r'(\d+)[．.]\s*([^A-D]+)(?=[A-D][．.、])',  # 标准格式：1. 题目内容A.
            'bracket': r'\((\d+)\)\s*([^A-D]+)(?=[A-D][．.、]|\([A-D]\))',  # 括号格式：(1) 题目内容(A)
            'dot': r'(\d+)\.\s*([^A-D]+)(?=[A-D][．.、])',  # 点号格式：1. 题目内容A.
        }
        
        # 选项模式
        self.option_patterns = [
            r'([A-D])[．.、]\s*(.*?)(?=[A-D][．.、]|$)',  # A. 选项内容
            r'\(([A-D])\)\s*(.*?)(?=\([A-D]\)|$)',  # (A) 选项内容
            r'([A-D])[：:]\s*(.*?)(?=[A-D][：:]|$)',  # A: 选项内容
        ]
        
        # 答案模式
        self.answer_patterns = [
            r'[（(]([A-D])[）)]',  # (A)
            r'正确答案[：:]\s*([A-D])',  # 正确答案：A
            r'答案[：:]\s*([A-D])',  # 答案：A
            r'[\s：:]+([A-D])$',  # 结尾处的A
        ]
        
        logger.info("格式转换器初始化完成")
    
    @staticmethod
    def get_format_types():
        """
        获取支持的格式类型
        
        Returns:
            dict: 格式类型和描述
        """
        return {
            '1': '标准格式 (1．题目内容 A.选项1 B.选项2...)',
            '2': '括号格式 ((1) 题目内容 (A)选项1 (B)选项2...)',
            '3': '点号格式 (1. 题目内容 A.选项1 B.选项2...)',
            '4': '自动识别格式'
        }
    
    def process_document(self, file_path: str = None, format_type: str = '4') -> Tuple[bool, Dict[str, Any]]:
        """
        处理文档并将题目信息存入数据库
        
        Args:
            file_path (str, optional): 文档路径，如果不提供则使用self.document的文件路径
            format_type (str, optional): 格式类型
            
        Returns:
            tuple: (是否成功, 结果统计)
        """
        try:
            # 如果没有提供文件路径，使用document的文件路径
            if not file_path and self.document:
                file_path = self.document.file.path
            
            if not file_path or not os.path.exists(file_path):
                logger.error(f"文件不存在: {file_path}")
                return False, {"error": "文件不存在"}
            
            # 更新任务状态为处理中
            if self.task:
                self.task.status = 'processing'
                self.task.save()
            
            # 读取文档内容
            content = self._read_document(file_path)
            if not content:
                logger.error("无法读取文档内容")
                if self.task:
                    self.task.status = 'failed'
                    self.task.error_message = "无法读取文档内容"
                    self.task.save()
                return False, {"error": "无法读取文档内容"}
            
            # 预处理内容
            content = self._preprocess_content(content)
            
            # 分割成题目块
            question_blocks = self._split_into_question_blocks(content, format_type)
            if not question_blocks:
                logger.error("未找到有效的题目块")
                if self.task:
                    self.task.status = 'failed'
                    self.task.error_message = "未找到有效的题目块"
                    self.task.save()
                return False, {"error": "未找到有效的题目块"}
            
            # 提取题目信息
            questions = []
            for block in question_blocks:
                extracted = self._extract_question(block, format_type)
                if extracted:
                    questions.append(extracted)
            
            if not questions:
                logger.error("未能从文档中提取到任何题目")
                if self.task:
                    self.task.status = 'failed'
                    self.task.error_message = "未能提取到任何题目"
                    self.task.save()
                return False, {"error": "未能提取到任何题目"}
            
            # 将提取的题目转换为系统格式并保存到数据库
            result = self._save_questions(questions)
            
            # 更新任务状态为已完成
            if self.task:
                self.task.status = 'completed'
                self.task.questions_count = result['total']
                self.task.completed_at = timezone.now()
                self.task.save()
            
            logger.info(f"文档处理完成，共处理 {result['total']} 个题目")
            return True, result
            
        except Exception as e:
            logger.error(f"处理文档时出错: {str(e)}")
            if self.task:
                self.task.status = 'failed'
                self.task.error_message = str(e)
                self.task.save()
            return False, {"error": str(e)}
    
    def _read_document(self, file_path: str) -> str:
        """
        读取文档内容
        
        Args:
            file_path (str): 文档路径
            
        Returns:
            str: 文档内容
        """
        try:
            # 获取文件扩展名
            ext = os.path.splitext(file_path)[1].lower()
            
            # 根据文件类型选择不同的读取方法
            if ext == '.txt':
                with open(file_path, 'r', encoding='utf-8') as f:
                    return f.read()
            elif ext == '.pdf':
                # 使用document_parsers中的PDFParser
                from .document_parsers import PDFParser
                parser = PDFParser(self.document, self.task)
                parser.parse()
                # 获取所有解析出的题目内容
                questions = ParsedQuestion.objects.filter(document=self.document)
                return "\n\n".join([q.content for q in questions])
            elif ext in ['.doc', '.docx']:
                # 使用document_parsers中的DocxParser
                from .document_parsers import DocxParser
                parser = DocxParser(self.document, self.task)
                parser.parse()
                # 获取所有解析出的题目内容
                questions = ParsedQuestion.objects.filter(document=self.document)
                return "\n\n".join([q.content for q in questions])
            else:
                logger.error(f"不支持的文件类型: {ext}")
                return ""
        except Exception as e:
            logger.error(f"读取文档时出错: {str(e)}")
            return ""
    
    def _preprocess_content(self, content: str) -> str:
        """
        预处理文档内容
        
        Args:
            content (str): 文档内容
            
        Returns:
            str: 预处理后的内容
        """
        if not content:
            return content
        
        # 统一换行符
        content = content.replace('\r\n', '\n').replace('\r', '\n')
        
        # 移除微信公众号等无关信息
        content = re.sub(r'微信公众号[：:][^\n]+\n?', '', content)
        
        # 移除标题和章节信息
        content = re.sub(r'第[一二三四五六七八九十]+章.*?\n', '', content)
        content = re.sub(r'(?:^|\n)\s*[一二三四五六七八九十]+、.*?\n', '', content)
        
        # 统一标点符号
        content = content.replace('（', '(').replace('）', ')')
        content = content.replace('：', ':').replace('．', '.')
        content = content.replace('，', ',').replace('；', ';')
        
        # 处理选项格式
        content = re.sub(r'([A-D])\s*[.:：、]\s*', r'\1. ', content)
        content = re.sub(r'\(\s*([A-D])\s*\)', r'(\1)', content)
        
        # 移除多余空格和空行
        content = re.sub(r'\s+', ' ', content)
        content = re.sub(r'\n{3,}', '\n\n', content)
        
        return content.strip()
    
    def _split_into_question_blocks(self, content: str, format_type: str) -> List[str]:
        """
        将内容分割成题目块
        
        Args:
            content (str): 文档内容
            format_type (str): 格式类型
            
        Returns:
            list: 题目块列表
        """
        if not content:
            return []
        
        # 根据格式类型选择分隔模式
        if format_type == '1':  # 标准格式
            pattern = r'(?=\d+[．.]\s*)'
        elif format_type == '2':  # 括号格式
            pattern = r'(?=\(\d+\)\s*)'
        elif format_type == '3':  # 点号格式
            pattern = r'(?=\d+\.\s*)'
        else:  # 自动识别
            pattern = r'(?=\d+[．.]\s*|\(\d+\)\s*|\d+\.\s*)'
        
        # 分割内容
        blocks = re.split(pattern, content)
        
        # 过滤无效块
        valid_blocks = []
        for block in blocks:
            block = block.strip()
            if block and len(block) > 20:  # 忽略太短的块
                valid_blocks.append(block)
        
        logger.info(f"分割得到 {len(valid_blocks)} 个题目块")
        return valid_blocks
    
    def _extract_question(self, block: str, format_type: str) -> Optional[Dict[str, Any]]:
        """
        从题目块中提取题目信息
        
        Args:
            block (str): 题目块内容
            format_type (str): 格式类型
            
        Returns:
            dict: 题目信息字典
        """
        try:
            # 根据格式类型选择题目模式
            if format_type == '1':  # 标准格式
                title_pattern = self.format_patterns['standard']
            elif format_type == '2':  # 括号格式
                title_pattern = self.format_patterns['bracket']
            elif format_type == '3':  # 点号格式
                title_pattern = self.format_patterns['dot']
            else:  # 自动识别
                for pattern_key in ['standard', 'bracket', 'dot']:
                    match = re.search(self.format_patterns[pattern_key], block, re.DOTALL)
                    if match:
                        title_pattern = self.format_patterns[pattern_key]
                        break
                else:
                    logger.warning(f"无法识别题目格式: {block[:50]}...")
                    return None
            
            # 提取题号和题目文本
            title_match = re.search(title_pattern, block, re.DOTALL)
            if not title_match:
                logger.warning(f"无法提取题目: {block[:50]}...")
                return None
            
            question_number = title_match.group(1)
            question_text = title_match.group(2).strip()
            
            # 提取选项
            options = {}
            for pattern in self.option_patterns:
                matches = list(re.finditer(pattern, block, re.DOTALL))
                if len(matches) == 4:  # 确保有4个选项
                    for match in matches:
                        options[match.group(1)] = match.group(2).strip()
                    break
            
            # 如果未能提取到4个选项，尝试替代方法
            if len(options) != 4:
                options = self._extract_options_alternative(block)
            
            # 确保有ABCD四个选项
            if len(options) != 4 or not all(key in options for key in ['A', 'B', 'C', 'D']):
                logger.warning(f"题目 {question_number} 选项不完整")
                # 添加空选项
                for key in ['A', 'B', 'C', 'D']:
                    if key not in options:
                        options[key] = ""
            
            # 提取答案
            answer = None
            for pattern in self.answer_patterns:
                answer_match = re.search(pattern, block)
                if answer_match:
                    answer = answer_match.group(1)
                    break
            
            if not answer:
                # 查找独立的答案标记
                answer_text = re.search(r'答案[:：]\s*([A-D])', block)
                if answer_text:
                    answer = answer_text.group(1)
                else:
                    # 默认使用A作为答案
                    answer = 'A'
                    logger.warning(f"题目 {question_number} 未找到答案，默认设为A")
            
            # 构建题目字典
            question = {
                'question_number': question_number,
                'question_text': question_text,
                'options': options,
                'answer': answer,
                'difficulty': 3,  # 默认难度中等
                'type': '单选题',  # 默认单选题
                'tags': self._extract_tags(block)
            }
            
            return question
            
        except Exception as e:
            logger.error(f"提取题目信息时出错: {str(e)}")
            return None
    
    def _extract_options_alternative(self, block: str) -> Dict[str, str]:
        """
        使用替代方法提取选项
        
        Args:
            block (str): 题目块内容
            
        Returns:
            dict: 选项字典
        """
        options = {}
        
        # 尝试提取选项
        for key in ['A', 'B', 'C', 'D']:
            # 尝试多种模式
            for pattern in [
                fr'{key}[.．、]\s*(.*?)(?=[A-D][.．、]|$)',
                fr'\({key}\)\s*(.*?)(?=\([A-D]\)|$)',
                fr'{key}[：:]\s*(.*?)(?=[A-D][：:]|$)',
                fr'{key}\s+(.*?)(?=[A-D]\s+|$)',
            ]:
                match = re.search(pattern, block, re.DOTALL)
                if match:
                    options[key] = match.group(1).strip()
                    break
        
        return options
    
    def _extract_tags(self, block: str) -> List[str]:
        """
        从题目块中提取标签
        
        Args:
            block (str): 题目块内容
            
        Returns:
            list: 标签列表
        """
        tags = []
        
        # 从题目内容推断标签
        if '政治' in block or '马克思' in block or '毛泽东' in block:
            tags.append('政治')
        if '经济' in block or '市场' in block or '价格' in block:
            tags.append('经济')
        if '历史' in block or '古代' in block or '近代' in block:
            tags.append('历史')
        if '地理' in block or '地形' in block or '气候' in block:
            tags.append('地理')
        if '文学' in block or '诗歌' in block or '小说' in block:
            tags.append('文学')
        if '数学' in block or '几何' in block or '代数' in block:
            tags.append('数学')
        if '物理' in block or '力学' in block or '电学' in block:
            tags.append('物理')
        if '化学' in block or '元素' in block or '化合物' in block:
            tags.append('化学')
        if '生物' in block or '细胞' in block or '遗传' in block:
            tags.append('生物')
        
        # 如果没有找到任何标签，添加默认标签
        if not tags:
            tags.append('选择题')
        
        return tags
    
    def _save_questions(self, questions: List[Dict[str, Any]]) -> Dict[str, Any]:
        """
        将提取的题目保存到数据库
        
        Args:
            questions (list): 提取的题目列表
            
        Returns:
            dict: 结果统计
        """
        if not self.document:
            logger.error("未提供文档对象，无法保存题目")
            return {"total": 0, "success": 0, "error": "未提供文档对象"}
        
        total = len(questions)
        success = 0
        
        # 先删除该文档之前解析的题目
        ParsedQuestion.objects.filter(document=self.document).delete()
        
        # 保存新题目
        for q in questions:
            try:
                # 构建选项内容
                options_text = ""
                for key, value in q['options'].items():
                    options_text += f"{key}. {value}\n"
                
                # 构建完整内容
                content = f"{q['question_text']}\n\n{options_text}"
                
                # 创建ParsedQuestion对象
                parsed_question = ParsedQuestion(
                    document=self.document,
                    content=content,
                    answer=q['answer'],
                    detected_answer=q['answer'],
                    page_number=1,  # 默认页码
                    question_type=q['type']
                )
                parsed_question.save()
                success += 1
                
            except Exception as e:
                logger.error(f"保存题目时出错: {str(e)}")
        
        return {
            "total": total,
            "success": success,
            "failed": total - success
        }
    
    def validate_questions(self, questions: List[Dict[str, Any]]) -> List[str]:
        """
        验证题目格式
        
        Args:
            questions (list): 题目列表
            
        Returns:
            list: 错误信息列表
        """
        errors = []
        
        for i, q in enumerate(questions):
            # 检查题号
            if 'question_number' not in q or not q['question_number'].isdigit():
                errors.append(f"题目 {i+1} 缺少有效题号")
            
            # 检查题目内容
            if 'question_text' not in q or not q['question_text']:
                errors.append(f"题目 {i+1} 缺少内容")
            
            # 检查选项
            if 'options' not in q or not isinstance(q['options'], dict):
                errors.append(f"题目 {i+1} 缺少有效选项")
            elif not all(key in q['options'] for key in ['A', 'B', 'C', 'D']):
                errors.append(f"题目 {i+1} 选项不完整，应包含A、B、C、D四个选项")
            
            # 检查答案
            if 'answer' not in q or q['answer'] not in ['A', 'B', 'C', 'D']:
                errors.append(f"题目 {i+1} 缺少有效答案，应为A、B、C、D之一")
        
        return errors 